package io.geekidea.boot.auth.service.impl;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.hwtx.form.domain.meta.ApiMeta;
import com.hwtx.form.domain.service.RacService;
import com.hwtx.form.domain.service.ResourceService;
import com.hwtx.form.domain.service.TenantsService;
import io.geekidea.boot.auth.dto.LoginDto;
import io.geekidea.boot.auth.service.LoginRedisService;
import io.geekidea.boot.auth.service.LoginService;
import io.geekidea.boot.auth.util.LoginUtil;
import io.geekidea.boot.auth.util.TokenUtil;
import io.geekidea.boot.auth.vo.LoginTokenVo;
import io.geekidea.boot.auth.vo.LoginVo;
import io.geekidea.boot.common.enums.SystemType;
import io.geekidea.boot.framework.exception.BusinessException;
import io.geekidea.boot.framework.exception.LoginException;
import io.geekidea.boot.system.entity.SysMenu;
import io.geekidea.boot.system.entity.SysUser;
import io.geekidea.boot.system.enums.SysMenuType;
import io.geekidea.boot.system.mapper.SysMenuMapper;
import io.geekidea.boot.system.mapper.SysUserMapper;
import io.geekidea.boot.util.IpRegionUtil;
import io.geekidea.boot.util.IpUtil;
import io.geekidea.boot.util.PasswordUtil;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author geekidea
 **/
@Slf4j
@Service
public class LoginServiceImpl implements LoginService {

    @Resource
    private SysUserMapper sysUserMapper;
    @Resource
    private RacService racService;
    @Resource
    private SysMenuMapper sysMenuMapper;
    @Resource
    private LoginRedisService loginRedisService;
    @Resource
    private TenantsService tenantsService;
    @Resource
    private ApiMeta apiMeta;
    @Resource
    private ResourceService resourceService;

    @Override
    public LoginTokenVo login(LoginDto dto, String locale) {
        String username = dto.getUsername();
        SysUser sysUser = sysUserMapper.getSysUserByUsername(username);
        if (sysUser == null) {
            throw new BusinessException("用户信息不存在");
        }
        // 用户ID
        Long userId = sysUser.getId();
        // 校验密码
        String dbPassword = sysUser.getPassword();
        String dbSalt = sysUser.getSalt();
        String password = dto.getPassword();
        String encryptPassword = PasswordUtil.encrypt(password, dbSalt);
        if (!encryptPassword.equals(dbPassword)) {
            throw new BusinessException("账号密码错误");
        }
        String token = TokenUtil.generateAdminToken(userId);
        LoginVo loginVo = refreshLoginInfo(sysUser, token, locale);
        LoginTokenVo loginTokenVo = new LoginTokenVo();
        loginTokenVo.setToken(token);
        loginTokenVo.setLoginVo(loginVo);
        return loginTokenVo;
    }

    @Override
    public LoginVo refreshLoginInfo(SysUser sysUser, String token, String locale) {
        // 用户ID
        Long userId = sysUser.getId();
        // 校验用户状态
        Boolean status = sysUser.getStatus();
        if (!status) {
            throw new BusinessException("用户已禁用");
        }
        // 查询用户角色
        List<Long> roleIds = Lists.newArrayList();
        if (!sysUser.getIsAdmin()) {
            roleIds = racService.getAllRoleIdsByUserId(userId);
            if (CollectionUtils.isEmpty(roleIds)) {
                throw new BusinessException("该用户不存在可用的角色");
            }
        }
        // 设置登录用户对象
        LoginVo loginVo = new LoginVo();
        BeanUtils.copyProperties(sysUser, loginVo);
        loginVo.setUserId(userId);
        loginVo.setAdmin(sysUser.getIsAdmin());
        loginVo.setRoleIds(roleIds);
        // 系统类型 1：管理后台，2：用户端
        loginVo.setSystemType(SystemType.ADMIN.getCode());
        loginVo.setTenantCode(tenantsService.getTenantCode(Lists.newArrayList(roleIds), userId));
        // 获取登录用户的权限编码
        Set<SysMenu> permissions = Sets.newHashSet(sysMenuMapper.getResourceCodeAndIdByUserRole(userId,
                Lists.newArrayList(SysMenuType.FUN_PERMISSION.getCode())));
        permissions.addAll(sysMenuMapper.getResourceCodeAndIdByUserOrgRole(userId, List.of(SysMenuType.FUN_PERMISSION.getCode())));
        Map<Long, Map<String, Long>> formFunPerms = Maps.newHashMap();
        Map<String, Long> urlFunPerms = Maps.newHashMap();
        Set<String> adminAuthApi = Sets.newHashSet();
        if (!sysUser.getIsAdmin()) {
            if (CollectionUtils.isEmpty(permissions)) {
                throw new BusinessException("用户未赋予权限");
            }
            Map<Long, Long> resIdAndFormId = permissions.stream().collect(Collectors.toMap(SysMenu::getId, SysMenu::getFId));
            resourceService.getResourceUrlMapping(resIdAndFormId.keySet()).stream().filter(mapping -> StringUtils.isNotBlank(mapping.getUrl()))
                    .forEach(res -> {
                        if (apiMeta.isAdminAuthApi(res.getUrl())) {
                            adminAuthApi.add(res.getUrl());
                        } else if (resourceService.containUniqueUrl(res.getUrl())) {
                            urlFunPerms.put(res.getUrl(), res.getResourceId());
                        } else {
                            Long formId = resIdAndFormId.get(res.getResourceId());
                            if (res.getFromFormId() != null) {
                                formId = res.getFromFormId();
                            }
                            formFunPerms.compute(formId, (fId, urlAndCode) -> {
                                if (urlAndCode == null) {
                                    urlAndCode = Maps.newHashMap();
                                }
                                urlAndCode.put(res.getUrl(), res.getResourceId());
                                return urlAndCode;
                            });
                        }
                    });
            if (formFunPerms.isEmpty() && urlFunPerms.isEmpty() && !loginVo.isAdmin()) {
                throw new BusinessException("用户没有权限");
            }
        }
        loginVo.setFormFunPerms(formFunPerms);
        loginVo.setUrlFunPerms(urlFunPerms);
        loginVo.setAdminAuthApi(adminAuthApi);
        loginVo.setLocale(locale);
        String requestIp = IpUtil.getRequestIp();
        String ipAreaDesc = IpRegionUtil.getIpAreaDesc(requestIp);
        // 设置最后登录时间
        sysUser.setLastLoginTime(new Date());
        // 设置最后登录IP
        sysUser.setLastLoginIp(requestIp);
        // 设置最后登录IP区域
        sysUser.setLastLoginIpArea(ipAreaDesc);
        sysUserMapper.updateById(sysUser);
        // 保存登录信息到redis中
        loginRedisService.setLoginVo(token, loginVo);
        return loginVo;
    }

    @Override
    public LoginVo getLoginUserInfo() {
        LoginVo loginVo = LoginUtil.getLoginVo();
        if (loginVo == null) {
            throw new LoginException("请先登录");
        }
        // 根据用户ID获取用户信息
        Long userId = loginVo.getUserId();
        // 获取用户登录时间
        SysUser sysUser = sysUserMapper.selectById(userId);
        if (sysUser == null) {
            throw new BusinessException("用户信息不存在");
        }
        // 刷新登录信息
        String token = TokenUtil.getToken();
        loginVo = refreshLoginInfo(sysUser, token, loginVo.getLocale());
        return loginVo;
    }

    @Override
    public void logout() {
        try {
            // 获取token
            String token = TokenUtil.getToken();
            // 删除缓存
            loginRedisService.deleteLoginVo(token);
        } catch (Exception e) {
            log.error("退出登录异常", e);
        }
    }
}
